﻿#ifndef _EASY_IOCP_SERVER_H_
#define _EASY_IOCP_SERVER_H_

#ifdef _WIN32

#include "EasyTcpServer.hpp"
#include "CELLIOCPServer.hpp"

class EasyIOCPServer : public EasyTcpServer
{
public:
	void Start(int nCELLServer)
	{
		EasyTcpServer::Start<CELLIOCPServer>(nCELLServer);
	}
private:
	// 处理网络消息
	virtual void OnRun(CELLThread* pthread) override
	{
		CELLIOCP iocp;
		iocp.loadAcceptEX(sockfd());
		iocp.create();
		iocp.reg(sockfd());
		// 接收新客户端时需要接收数据的缓冲区，理论上只需要下面这么大的空间
		// const int len = 2 * (sizeof(sockaddr_in) + 16);
		// 如果还要在接收连接的时候接收额外的数据就需要更大的空间，这里直接设置成了1024
		// 不需要客户端连接后立即发送数据的情况下是上面计算的len大小
		const int len = 1024;
		char buffer[len] = { 0 };
		IO_DATA_BASE ioData = { 0 };
		ioData.wsaBuff.buf = buffer;
		ioData.wsaBuff.len = len;
		// 
		IO_EVENT ioEvent;
		// 投递接收一个链接的任务
		iocp.postAccept(&ioData);
		time_t time;
		while (pthread->isRun())
		{
			time4msg();
			time = _time_events.getTime();
			if (time == 0)
			{
				time = INFINITE;
			}
			int ret = iocp.wait(ioEvent, time);
			if (ret < 0)
			{
				CELLLog_pError("EasyIOCPServer::OnRun error");
				pthread->Exit();
				break;
			}
			else if (ret == 0)
			{
				_time_events.process_timer();
				continue;
			}

			// 接受新连接
			if (IO_TYPE::ACCEPT == ioEvent.pIOData->iotype)
			{
				// 新客户端加入
				// CELLLog_Info("new client join sockfd=%d", (int)ioEvent.pIOData->sockfd);
				// 调用的是本类的IocpAccept客户端函数
				IocpAccept(ioData.sockfd);
				// 再次向IOCP投递接收新连接的任务
				/*
					错误记录：投递接收新连接任务如果放在IocpAccept之前会报很多错误
					!: 原因：投递出去的任务会直接完成，如果超出了最大限制数，
						但是还是会建立连接，再调用IocpAccept函数会直接关闭这个连接，
						然后造成一系列WSA函数的报错
				*/
				iocp.postAccept(&ioData);
			}
			_time_events.process_timer();
		}
		CELLLog_Info("EasyIOCPServer OnRun exit");
	}

	/*
		接收客户端连接
		? 为什么IOCP要自己写一个接收新连接的函数
			: IOCP与select和epoll的机制不同，后两者是提醒可以接收连接，
				前者(IOCP)是已经连接上了再通知已经连接
	*/
	SOCKET IocpAccept(SOCKET cSock)
	{
		// 在这里可以取出ip地址，做别的事情
		if (cSock == INVALID_SOCKET)
		{
			// 理论上不会触发
			CELLLog_pError("EasyIOCPServer IocpAccept error on line %d", __LINE__);
		}
		else
		{
			if (_clientCount < _nMaxClient)
			{
				AddClientToCELLServer(new CELLClient(cSock, _nSendBuffSize, _nRecvBuffSize));
			}
			else
			{
				CELLNetWork::destorySocket(cSock);
				CELLLog_Warning("IocpAccept to nMaxClient");
			}
		}
		return cSock;
	}
};

#endif // #ifdef _WIN32

#endif
